<script lang="ts" setup>
import { computed, onMounted, ref } from 'vue';

import { Page } from '@vben/common-ui';

import { Button, Card, Input, List, Modal, Progress, message } from 'ant-design-vue';

import {
  checkChunkStatus,
  confirmUpload as confirmUploadApi,
  getFileList,
  uploadChunk,
} from '#/api/core/upload';

// 配置
const CHUNK_SIZE = 5 * 1024 * 1024; // 5MB
const UPLOAD_DELAY = 250; // 每个分片上传间隔（毫秒），控制上传速度避免触发频率限制
const MAX_CONCURRENT_UPLOADS = 3; // 最大并发上传数

// 状态变量
const fileInputRef = ref<HTMLInputElement | null>(null);
const selectedFile = ref<File | null>(null);
const fileIdentifier = ref<string>('');
const totalChunks = ref<number>(0);
const uploadedChunks = ref<number[]>([]);
const isUploading = ref<boolean>(false);
const isPaused = ref<boolean>(false);
const isDragOver = ref<boolean>(false);
const progress = ref<number>(0);
const uploadedFilePath = ref<string | null>(null);
const isUploadComplete = ref<boolean>(false);
const targetDirectory = ref<string>('');
const fileList = ref<string[]>([]);

// 计算属性
const uploadStatus = computed<'success' | 'exception' | 'active' | 'normal'>(() => {
  if (isUploadComplete.value) return 'success';
  if (isPaused.value) return 'exception';
  return 'active';
});

// 生命周期钩子
onMounted(() => {
  loadFileList();
});

// 方法
function triggerFileInput() {
  fileInputRef.value?.click();
}

function handleDragOver(_e: DragEvent) {
  isDragOver.value = true;
}

function handleDragLeave() {
  isDragOver.value = false;
}

function handleDrop(e: DragEvent) {
  isDragOver.value = false;
  
  if (e.dataTransfer?.files.length) {
    const file = e.dataTransfer.files[0];
    if (file) {
      handleFile(file);
    }
  }
}

function handleFileSelect(e: Event) {
  const target = e.target as HTMLInputElement;
  if (target.files?.length) {
    const file = target.files[0];
    if (file) {
      handleFile(file);
    }
  }
}

function handleFile(file: File) {
  selectedFile.value = file;
  
  // 生成文件标识符
  fileIdentifier.value = Date.now() + '-' + file.name.replace(/[^a-zA-Z0-9]/g, '');
  
  // 计算总分片数
  totalChunks.value = Math.ceil(file.size / CHUNK_SIZE);
  
  // 重置上传状态
  uploadedChunks.value = [];
  progress.value = 0;
  isUploading.value = false;
  isPaused.value = false;
  isUploadComplete.value = false;
  uploadedFilePath.value = null;
}

async function startUpload() {
  if (!selectedFile.value || (isUploading.value && !isPaused.value)) return;
  
  isUploading.value = true;
  isPaused.value = false;
  
  try {
    // 检查已上传的分片
    await checkExistingChunks();
    
    // 上传分片
    await uploadChunks();
    
    if (!isPaused.value) {
      // 上传完成
      uploadComplete();
    }
  } catch (error: any) {
    console.error('上传出错:', error);
    message.error('上传过程中发生错误: ' + error.message);
    resetUploadState();
  }
}

async function checkExistingChunks() {
  if (!selectedFile.value) return;
  
  console.log(`检查已上传的分片状态，总共 ${totalChunks.value} 个分片`);
  
  // 分批检查分片状态，避免触发频率限制
  const batchSize = 10; // 每批检查10个分片
  const batches = [];
  
  for (let i = 0; i < totalChunks.value; i += batchSize) {
    const batch = [];
    for (let j = i; j < Math.min(i + batchSize, totalChunks.value); j++) {
      batch.push(j + 1);
    }
    batches.push(batch);
  }
  
  for (const batch of batches) {
    if (isPaused.value) break;
    
    // 并发检查当前批次的分片
    const checkPromises = batch.map(async (chunkNumber) => {
      try {
        const exists = await checkChunkStatus(fileIdentifier.value, chunkNumber);
        
        if (exists && !uploadedChunks.value.includes(chunkNumber)) {
          uploadedChunks.value.push(chunkNumber);
          console.log(`分片 ${chunkNumber} 已存在`);
        }
        
        return { chunkNumber, exists };
      } catch (error) {
        console.error(`检查分片 ${chunkNumber} 状态时出错:`, error);
        return { chunkNumber, exists: false };
      }
    });
    
    await Promise.all(checkPromises);
    updateProgress();
    
    // 批次间添加延迟，避免触发频率限制
    if (batches.indexOf(batch) < batches.length - 1) {
      await new Promise(resolve => setTimeout(resolve, 100));
    }
  }
  
  console.log(`检查完成，发现 ${uploadedChunks.value.length} 个已上传的分片`);
}

async function uploadChunks() {
  if (!selectedFile.value) return;
  
  // 获取需要上传的分片列表
  const chunksToUpload = [];
  for (let i = 1; i <= totalChunks.value; i++) {
    if (!uploadedChunks.value.includes(i)) {
      chunksToUpload.push(i);
    }
  }
  
  console.log(`需要上传 ${chunksToUpload.length} 个分片，总共 ${totalChunks.value} 个分片`);
  
  // 使用控制并发和延迟的方式上传分片
  await uploadChunksWithRateLimit(chunksToUpload);
}

/**
 * 带速度控制的分片上传
 * @param chunks 需要上传的分片编号数组
 */
async function uploadChunksWithRateLimit(chunks: number[]) {
  if (!selectedFile.value) return;
  
  const semaphore = new Array(MAX_CONCURRENT_UPLOADS).fill(null);
  let currentIndex = 0;
  
  const uploadPromises = semaphore.map(async () => {
    while (currentIndex < chunks.length && !isPaused.value) {
      const chunkIndex = currentIndex++;
      const chunkNumber = chunks[chunkIndex]!;
      
      try {
        console.log(`开始上传分片 ${chunkNumber}/${totalChunks.value}`);
        
        const result = await uploadChunk({
          file: selectedFile.value!,
          identifier: fileIdentifier.value,
          chunkNumber: chunkNumber,
          totalChunks: totalChunks.value,
          chunkSize: CHUNK_SIZE
        });
        
        uploadedChunks.value.push(chunkNumber);
        updateProgress();
        
        console.log(`分片 ${chunkNumber} 上传成功`);
        
        if (result.merged) {
          uploadedFilePath.value = result.filePath || null;
          console.log('所有分片已合并完成');
        }
        
        // 添加延迟控制，避免触发频率限制
        if (currentIndex < chunks.length && !isPaused.value) {
          console.log(`等待 ${UPLOAD_DELAY}ms 后继续上传下一个分片`);
          await new Promise(resolve => setTimeout(resolve, UPLOAD_DELAY));
        }
        
      } catch (error: any) {
        console.error(`上传分片 ${chunkNumber} 时出错:`, error);
        
        // 如果是频率限制错误，增加延迟时间
        if (error?.response?.status === 429 || error?.message?.includes('rate limit')) {
          console.warn('触发频率限制，等待更长时间后重试');
          await new Promise(resolve => setTimeout(resolve, UPLOAD_DELAY * 4));
          
          // 重新尝试上传这个分片
          currentIndex--;
          continue;
        }
        
        throw error;
      }
    }
  });
  
  await Promise.all(uploadPromises);
}

function updateProgress() {
  progress.value = Math.round((uploadedChunks.value.length / totalChunks.value) * 100);
}

function uploadComplete() {
  isUploading.value = false;
  
  if (uploadedChunks.value.length === totalChunks.value) {
    isUploadComplete.value = true;
    message.success('文件上传完成！');
  }
}

function pauseUpload() {
  isPaused.value = true;
}

async function resumeUpload() {
  isPaused.value = false;
  
  try {
    await uploadChunks();
    
    if (!isPaused.value && uploadedChunks.value.length === totalChunks.value) {
      uploadComplete();
    }
  } catch (error: any) {
    console.error('继续上传时出错:', error);
    message.error('上传过程中发生错误: ' + error.message);
  }
}

function cancelUpload() {
  Modal.confirm({
    title: '确认取消',
    content: '确定要取消当前上传吗？',
    onOk: () => {
      resetUploadState();
    }
  });
}

async function confirmUpload() {
  if (!selectedFile.value) return;
  
  try {
    const result = await confirmUploadApi(selectedFile.value.name, targetDirectory.value);
    
    if (result.success) {
      message.success('文件已成功移动到指定目录！');
      resetUploadState();
      loadFileList();
    } else {
      message.error('移动文件失败: ' + result.message);
    }
  } catch (error: any) {
    console.error('确认上传时出错:', error);
    message.error('确认上传时发生错误: ' + error.message);
  }
}

function resetUploadState() {
  selectedFile.value = null;
  fileIdentifier.value = '';
  totalChunks.value = 0;
  uploadedChunks.value = [];
  isUploading.value = false;
  isPaused.value = false;
  isUploadComplete.value = false;
  uploadedFilePath.value = null;
  progress.value = 0;
  targetDirectory.value = '';
  
  if (fileInputRef.value) {
    fileInputRef.value.value = '';
  }
}

async function loadFileList() {
  try {
    const result = await getFileList();
    // 确保结果是数组，如果不是则设置为空数组
    fileList.value = Array.isArray(result) ? result : [];
  } catch (error: any) {
    console.error('加载文件列表时出错:', error);
    
    // 出错时设置为空数组，避免显示异常
    fileList.value = [];
    
    // 改进错误处理，提供更友好的错误信息
    let errorMessage = '加载文件列表失败';
    
    // 检查是否是网络错误或请求错误
    if (error?.response) {
      // 有响应但状态码不是2xx
      const status = error.response.status;
      switch (status) {
        case 401:
          errorMessage = '认证失败，请重新登录';
          break;
        case 403:
          errorMessage = '权限不足，无法访问文件列表';
          break;
        case 404:
          errorMessage = '文件列表接口不存在，可能后端服务未启动';
          break;
        case 500:
        case 502:
        case 503:
          errorMessage = '服务器错误，请稍后重试';
          break;
        default:
          errorMessage = `服务器返回错误 (${status})`;
      }
    } else if (error?.request) {
      // 请求已发出但没有收到响应
      errorMessage = '网络连接失败，请检查网络或后端服务是否启动';
    } else if (error?.message && error.message !== 'undefined' && error.message.trim()) {
      // 其他错误
      errorMessage = `加载文件列表失败: ${error.message}`;
    } else {
      // 未知错误
      errorMessage = '加载文件列表失败，请稍后重试';
    }
    
    // 只有在非401错误时才显示错误消息，401错误通常会自动跳转登录
    if (error?.response?.status !== 401) {
      message.warning(errorMessage);
    }
  }
}

function promptMoveFile(filename: string) {
  Modal.confirm({
    title: '移动文件',
    content: '请输入目标目录（可选）:',
    onOk: () => moveFile(filename, targetDirectory.value)
  });
  
  targetDirectory.value = '';
}

async function moveFile(filename: string, targetDir: string) {
  try {
    const result = await confirmUploadApi(filename, targetDir);
    
    if (result.success) {
      message.success('文件已成功移动到指定目录！');
      loadFileList();
    } else {
      message.error('移动文件失败: ' + result.message);
    }
  } catch (error: any) {
    console.error('移动文件时出错:', error);
    message.error('移动文件时发生错误: ' + error.message);
  }
}

function formatFileSize(bytes: number): string {
  if (bytes === 0) return '0 Bytes';
  
  const k = 1024;
  const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  
  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
</script>

<template>
  <Page auto-content-height>
    <Card title="大文件上传示例">
      <div
        class="upload-container"
        :class="{ 'drag-over': isDragOver }"
        @dragover.prevent="handleDragOver"
        @dragleave.prevent="handleDragLeave"
        @drop.prevent="handleDrop"
      >
        <p>拖拽文件到此处或点击选择文件</p>
        <input
          ref="fileInputRef"
          type="file"
          class="file-input"
          @change="handleFileSelect"
        />
        <Button type="primary" @click="triggerFileInput">选择文件</Button>
      </div>

      <div v-if="selectedFile" class="file-info">
        <p>文件名: {{ selectedFile.name }}</p>
        <p>文件大小: {{ formatFileSize(selectedFile.size) }}</p>
        <p>文件类型: {{ selectedFile.type || '未知' }}</p>
      </div>

      <div v-if="selectedFile" class="progress-container">
        <p>上传进度: {{ progress }}%</p>
        <Progress :percent="progress" :status="uploadStatus" />
      </div>

      <div v-if="selectedFile" class="upload-actions">
        <Button
          type="primary"
          :disabled="isUploading && !isPaused"
          @click="startUpload"
        >
          开始上传
        </Button>
        <Button
          :disabled="!isUploading || isPaused"
          @click="pauseUpload"
        >
          暂停上传
        </Button>
        <Button
          :disabled="!isPaused"
          @click="resumeUpload"
        >
          继续上传
        </Button>
        <Button danger @click="cancelUpload">取消上传</Button>
      </div>

      <div v-if="isUploadComplete" class="confirm-actions">
        <p>文件上传完成！请指定目标目录（可选）并确认：</p>
        <Input
          v-model:value="targetDirectory"
          placeholder="目标目录（可选）"
          class="custom-dir-input"
        />
        <Button type="primary" @click="confirmUpload">确认并移动文件</Button>
      </div>

      <div class="file-list">
        <div class="file-list-header">
          <h2>已上传文件列表</h2>
          <Button @click="loadFileList">刷新列表</Button>
        </div>
        <div v-if="fileList.length === 0" class="empty-list">
          暂无上传文件
        </div>
        <List v-else :data-source="fileList" class="file-list-content">
          <template #renderItem="{ item }">
            <List.Item>
              <span>{{ item }}</span>
              <Button @click="promptMoveFile(item)">移动到自定义目录</Button>
            </List.Item>
          </template>
        </List>
      </div>
    </Card>
  </Page>
</template>

<style scoped>
.upload-container {
  border: 2px dashed #ccc;
  border-radius: 10px;
  padding: 20px;
  text-align: center;
  margin: 20px 0;
  background-color: #f9f9f9;
  transition: all 0.3s;
}

.upload-container.drag-over {
  border-color: #1890ff;
  background-color: #e6f7ff;
}

.file-input {
  display: none;
}

.file-info {
  margin: 20px 0;
  text-align: left;
}

.progress-container {
  margin: 20px 0;
}

.upload-actions {
  display: flex;
  gap: 10px;
  margin: 20px 0;
}

.confirm-actions {
  margin: 20px 0;
  padding: 15px;
  background-color: #f0f8ff;
  border-radius: 5px;
  border: 1px solid #d9e8f6;
}

.custom-dir-input {
  width: 300px;
  margin-right: 10px;
}

.file-list {
  margin-top: 30px;
  border-top: 1px solid #eee;
  padding-top: 20px;
}

.file-list-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 15px;
}

.file-list-content {
  background-color: #fff;
  border-radius: 5px;
}

.empty-list {
  text-align: center;
  padding: 20px;
  color: #999;
}
</style>
